﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using ABCat.Core;
using ABCat.Core.Plugins.DataSets;
using ABCat.Shared;
using ABCat.Shared.Properties;

namespace ABCat.Plugins.NormalizationSettingsEditor.Standard
{
	public sealed class NormalizationEditorViewModel : INotifyPropertyChanged
	{
		#region + Events +

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion
		#region + Fields +

		private static readonly IEnumerable<PropertyForSelect> _availableProperties = new[] 
			{
				new PropertyForSelect { DisplayName = "Битрейт", FieldName = "bitrate" },
				new PropertyForSelect { DisplayName = "Издатель", FieldName = "publisher" },
				new PropertyForSelect { DisplayName = "Читает", FieldName = "reader" },
				new PropertyForSelect { DisplayName = "Автор", FieldName = "author" },
				new PropertyForSelect { DisplayName = "Жанр", FieldName = "genre" }
			};

		private readonly Context _context;
		private readonly ObservableCollection<string> _valuesForReplace = new ObservableCollection<string>();

		private string _currentPossibleValue;

		private string _replacementValue;
		private PropertyForSelect _selectedProperty;
		private IEnumerable<IAudioBook> _targetRecords;
		private bool _isOnEditMode;
		private bool _isOnWaitMode = true;

		#endregion
		#region + Properties +

		public IEnumerable<PropertyForSelect> AvailableProperties
		{
			get { return _availableProperties; }
		}

		public string CurrentPossibleValue
		{
			get { return _currentPossibleValue; }
			set
			{
				if (value == _currentPossibleValue) return;
				_currentPossibleValue = value;
				CommandManager.InvalidateRequerySuggested();
				OnPropertyChanged();
			}
		}

		public string ReplacementValue
		{
			get { return _replacementValue; }
			set
			{
				if (value == _replacementValue) return;
				_replacementValue = value;
				CommandManager.InvalidateRequerySuggested();
				OnPropertyChanged();
			}
		}

		private readonly DelegateCommand _saveCommand;
		private readonly ICommand _cancelCommand;
		private readonly ICommand _currentAsReplacementValueCommand;
		private readonly DelegateCommand _removeItemCommand;

		public DelegateCommand SaveCommand
		{
			get
			{
				return _saveCommand;
			}
		}

		public DelegateCommand RemoveItemCommand
		{
			get
			{
				return _removeItemCommand;
			}
		}

		public ICommand CurrentAsReplacementValueCommand
		{
			get
			{
				return _currentAsReplacementValueCommand;
			}
		}

		public PropertyForSelect SelectedProperty
		{
			get
			{
				return _selectedProperty;
			}
			set
			{
				if (value == _selectedProperty) return;
				_selectedProperty = value;
				UpdateValuesForReplace();
				OnPropertyChanged();
			}
		}

		public IEnumerable<IAudioBook> TargetRecords
		{
			get { return _targetRecords; }
			set
			{
				_targetRecords = value;
				if (SelectedProperty == null) SelectedProperty = AvailableProperties.First();
				UpdateValuesForReplace();
				IsOnEditMode = _targetRecords.AnySafe();
			}
		}

		public bool IsOnWaitMode
		{
			get { return _isOnWaitMode; }
			set
			{
				if (value.Equals(_isOnWaitMode)) return;
				_isOnWaitMode = value;
				IsOnEditMode = !IsOnWaitMode;
				OnPropertyChanged();
			}
		}

		public bool IsOnEditMode
		{
			get { return _isOnEditMode; }
			set
			{
				if (value.Equals(_isOnEditMode)) return;
				_isOnEditMode = value;
				IsOnWaitMode = !IsOnEditMode;
				OnPropertyChanged();
			}
		}

		public ObservableCollection<string> ValuesForReplace
		{
			get { return _valuesForReplace; }
		}

		public ICommand CancelCommand
		{
			get { return _cancelCommand;}
		}

		#endregion
		#region + Ctor +

		public NormalizationEditorViewModel(Context context)
		{
			_context = context;
			_saveCommand = new DelegateCommand(Save, IsCanSave);
			_cancelCommand = new DelegateCommand((parameters) => 
			{
				ValuesForReplace.Clear();
				ReplacementValue = null;
				IsOnEditMode = false;
				OnPropertyChanged("IsOnEditMode");
			}, (parameters) => true);

			_currentAsReplacementValueCommand = new DelegateCommand((parameters) =>
				{
					ReplacementValue = CurrentPossibleValue;
				}, (parameters) => CurrentPossibleValue != null);

			_removeItemCommand = new DelegateCommand((parameter) => ValuesForReplace.Remove(parameter.ToString()), (parameter) => true);
		}

		#endregion
		#region + Logic +

		[NotifyPropertyChangedInvocator]
		private void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChangedEventHandler handler = PropertyChanged;
			if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
		}

		private bool IsCanSave(object arg)
		{
			return !string.IsNullOrEmpty(ReplacementValue) && ValuesForReplace.Any(item => String.Compare(item, ReplacementValue, StringComparison.OrdinalIgnoreCase) != 0);
		}

		private void Save(object obj)
		{
			using (var dbContainer = _context.CreateDbContainer(true))
			{
				var existed = dbContainer.ReplacementStringSet.GetReplacementStringsBy(SelectedProperty.FieldName).Where(item => string.Compare(item.ReplaceValue, ReplacementValue, StringComparison.OrdinalIgnoreCase) != 0).ToArray();

				var newReplacementStrings = new List<IReplacementString>();

				foreach (var valueForReplace in ValuesForReplace.Where(item => item != ReplacementValue))
				{
					if (existed.All(item => string.Compare(item.PossibleValue, valueForReplace, StringComparison.OrdinalIgnoreCase) != 0))
					{
						var replacementString = dbContainer.ReplacementStringSet.CreateReplacementString();
						replacementString.PossibleValue = valueForReplace;
						replacementString.RecordPropertyName = SelectedProperty.FieldName;
						replacementString.ReplaceValue = ReplacementValue;
						newReplacementStrings.Add(replacementString);
					}
				}

				dbContainer.ReplacementStringSet.AddReplacementString(newReplacementStrings.ToArray());

				TargetRecords = new IAudioBook[0];
			}
		}

		private void UpdateValuesForReplace()
		{
			ValuesForReplace.Clear();

			foreach (var targetRecord in _targetRecords)
			{
				string value = null;
				switch (SelectedProperty.FieldName)
				{
					case "bitrate":
						value = targetRecord.Bitrate;
						break;
					case "publisher":
						value = targetRecord.Publisher;
						break;
					case "reader":
						value = targetRecord.Reader;
						break;
					case "author":
						value = targetRecord.Author;
						break;
					case "genre":
						value = targetRecord.Genre;
						break;
				}

				if (!string.IsNullOrEmpty(value) && ValuesForReplace.All(item => String.Compare(item, value, StringComparison.OrdinalIgnoreCase) != 0))
				{
					ValuesForReplace.Add(value);
				}
			}
		}

		#endregion
		#region # Nested Classes #

		public class PropertyForSelect
		{
			#region + Properties +

			public string DisplayName { get; set; }
			public string FieldName { get; set; }

			#endregion
			#region + Logic +

			public override string ToString()
			{
				return DisplayName;
			}

			#endregion
		}

		#endregion
	}
}